from dataclasses import dataclass, field
from datetime import datetime
import threading
from typing import Dict, List, Optional
import queue
import uuid
import os
import glob
from PIL import Image
import shutil
from app.models.image_processor import ImageProcessor
from app.models.quality_checker import QualityChecker
import cv2
import numpy as np
import time
from app.models.common import FilterReason
import logging
import json
from pathlib import Path
import hashlib
import face_recognition
from concurrent.futures import ThreadPoolExecutor, as_completed
from app.models.db import DBConnection
from PySide6.QtCore import QObject, Signal, QTimer

@dataclass
class ImageInfo:
    """图片基本信息"""
    size: int
    status: str = "wait"  # wait, success, failed
    hash: str = ""
    timestamp: datetime = field(default_factory=datetime.now)

    def to_dict(self):
        return {
            "size": self.size,
            "status": self.status,
            "hash": self.hash,
            "timestamp": self.timestamp.isoformat() if isinstance(self.timestamp, datetime) else self.timestamp
        }

@dataclass
class ImageAnalysis:
    """图片分析结果"""
    laplacian_score: float = 0
    contrast_score: float = 0
    face_ratio_score: float = 0
    brightness_score: float = 0
    prompt: str = ""  # full_body, upper_body, cropped_arms
    
    @property
    def quality_score(self) -> float:
        """计算综合质量分数"""
        weights = {
            'laplacian': 0.3,
            'contrast': 0.3,
            'face_ratio': 0.2,
            'brightness': 0.2
        }
        return (
            self.laplacian_score * weights['laplacian'] +
            self.contrast_score * weights['contrast'] +
            self.face_ratio_score * weights['face_ratio'] +
            self.brightness_score * weights['brightness']
        )

    def to_dict(self):
        return {
            "quality_score": self.quality_score,
            "laplacian_score": self.laplacian_score,
            "contrast_score": self.contrast_score,
            "face_ratio_score": self.face_ratio_score,
            "brightness_score": self.brightness_score,
            "prompt": self.prompt
        }

@dataclass
class ProcessResult:
    """处理结果"""
    status: str = "wait"  # wait, success, failed
    analysis: Optional[ImageAnalysis] = None
    processed_path: str = ""
    thumbnails_path: str = ""
    horizontal_reversal_path: str = ""
    filter_reasons: List[Dict] = field(default_factory=list)
    timestamp: datetime = field(default_factory=datetime.now)

    def to_dict(self):
        return {
            "status": self.status,
            "analysis": self.analysis.to_dict() if self.analysis else None,
            "processed_path": self.processed_path,
            "thumbnails_path": self.thumbnails_path,
            "horizontal_reversal_path": self.horizontal_reversal_path,
            "filter_reasons": self.filter_reasons,
            "timestamp": self.timestamp.isoformat()
        }

    @classmethod
    def from_dict(cls, data: dict):
        """从字典创建实例"""
        if data.get('analysis'):
            data['analysis'] = ImageAnalysis(**data['analysis'])
        if 'timestamp' in data:
            data['timestamp'] = datetime.fromisoformat(data['timestamp'])
        return cls(**data)

class ProcessTask:
    """处理任务"""
    def __init__(self, task_id: str, input_dir: str, output_dir: str, config: 'ProcessConfig'):
        self.task_id = task_id
        self.input_dir = input_dir
        self.output_dir = output_dir
        self.config = config
        self.status = 'pending'  # pending, processing, paused, completed
        self.progress = 0
        self.total_files = 0
        self.processed_files = 0
        self.results: Dict[str, ProcessResult] = {}
        self.start_time = None
        self.end_time = None
        self.error = None
        
    @property
    def duration(self) -> float:
        """任务持续时间（秒）"""
        if not self.start_time:
            return 0
        end = self.end_time or datetime.now()
        return (end - self.start_time).total_seconds()

    def to_dict(self):
        """将任务转换为字典格式"""
        def convert_value(v):
            """转换numpy类型为Python原生类型"""
            if isinstance(v, (np.int_, np.intc, np.intp, np.int8, np.int16, np.int32, np.int64)):
                return int(v)
            elif isinstance(v, (np.float_, np.float16, np.float32, np.float64)):
                return float(v)
            elif isinstance(v, (np.bool_)):
                return bool(v)
            return v

        return {
            'task_id': self.task_id,
            'input_dir': self.input_dir,
            'output_dir': self.output_dir,
            'status': self.status,
            'progress': float(self.progress),
            'processed_files': int(self.processed_files),
            'total_files': int(self.total_files),
            'start_time': self.start_time.isoformat() if self.start_time else None,
            'end_time': self.end_time.isoformat() if self.end_time else None,
            'results': {
                filename: {
                    'status': result.status,
                    'filter_reasons': [{'code': r.code, 'message': r.message} for r in result.filter_reasons],
                    'analysis': {
                        k: convert_value(v)
                        for k, v in (result.analysis or {}).items()
                    },
                    'processed_path': result.processed_path,
                    'timestamp': result.timestamp.isoformat()
                } for filename, result in self.results.items()
            }
        }

class TaskManager(QObject):
    """任务管理器"""
    update_task_stats = Signal(str)  # 任务统计信息更新信号
    
    def __init__(self, config=None):
        super().__init__()
        self.logger = logging.getLogger(__name__)
        self.config_dir = Path.home() / '.lora_filter'
        self.tasks_file = self.config_dir / 'tasks.json'
        self.thumbnails_dir = self.config_dir / 'thumbnails'
        self.thumbnails_dir.mkdir(parents=True, exist_ok=True)
        
        # 保存配置
        self.config = config
        
        # 创建处理器
        self.image_processor = ImageProcessor()
        self.quality_checker = QualityChecker()
        
        # 控制标志
        self.running = True
        
        # 创建线程池
        self.executor = ThreadPoolExecutor(max_workers=4)
        
        # 创建定时器
        self.timer = QTimer()
        self.timer.setInterval(3000)  # 3秒
        self.timer.timeout.connect(self._process_queue)
        self.timer.start()

    def stop(self):
        """停止处理"""
        try:
            self.logger.info("正在停止任务管理器...")
            self.running = False
            
            # 停止定时器
            if hasattr(self, 'timer'):
                self.timer.stop()
                self.logger.info("定时器已停止")
            
            # 关闭线程池
            if hasattr(self, 'executor'):
                self.executor.shutdown(wait=True)
                self.logger.info("线程池已关闭")
            
        except Exception as e:
            self.logger.error(f"停止任务管理器失败: {e}", exc_info=True)

    def _process_queue(self):
        """处理任务队列（定时执行）"""
        try:
            # 获取所有处理中的任务
            sql = "SELECT * FROM tasks WHERE status = 'processing'"
            processing_tasks = DBConnection.execute(sql, fetch=True)
            
            if not processing_tasks:
                return
                
            for task in processing_tasks:
                if not self.running:
                    break
                    
                try:
                    task_id = task['task_id']
                    
                    # 获取待处理的图片列表
                    sql = """
                        SELECT * FROM image_list 
                        WHERE task_id = %s AND status = 'wait'
                        LIMIT 100
                    """
                    waiting_images = DBConnection.execute(sql, (task_id,), fetch=True)
                    
                    if not waiting_images:
                        continue
                        
                    for img in waiting_images:
                        if not self.running:
                            break
                            
                        try:
                            # 更新图片状态为处理中
                            sql = """
                                UPDATE image_list 
                                SET status = 'processing' 
                                WHERE task_id = %s AND filename = %s
                            """
                            DBConnection.execute(sql, (task_id, img['filename']))
                            
                            image_path = os.path.join(task['input_dir'], img['filename'])
                            if os.path.exists(image_path):
                                self._process_single_image(task_id, image_path)
                                
                        except Exception as e:
                            self.logger.error(f"处理图片失败: {e}", exc_info=True)
                            continue
                    
                except Exception as e:
                    self.logger.error(f"处理任务失败: {task_id}, 错误: {e}", exc_info=True)
                    continue
                    
        except Exception as e:
            self.logger.error(f"处理队列出错: {e}", exc_info=True)

    def _process_single_image(self, task_id: str, image_path: str):
        """处理单张图片"""
        try:
            # 获取任务信息
            sql = "SELECT * FROM tasks WHERE task_id = %s"
            task = DBConnection.execute(sql, (task_id,), fetch=True)[0]
            
            if task['status'] != 'processing':
                return
            
            filename = os.path.basename(image_path)
            
            # 处理图片
            result = self.image_processor.process_image(image_path)
            
            # 根据处理结果保存到不同的表
            if not result.filter_reasons:  # 处理通过
                sql = """
                    INSERT INTO success_results (
                        task_id, filename, processed_path, quality_score,
                        face_count, face_ratio, brightness_score,
                        contrast_score, blur_score
                    ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
                """
                params = (
                    task_id, filename, image_path,
                    result.analysis.quality_score,
                    result.face_count, result.face_ratio,
                    result.analysis.brightness_score,
                    result.analysis.contrast_score,
                    result.analysis.blur_score
                )
                DBConnection.execute(sql, params)
                
            else:  # 未通过
                sql = """
                    INSERT INTO failed_results (
                        task_id, filename, filter_code,
                        filter_message, filter_details
                    ) VALUES (%s, %s, %s, %s, %s)
                """
                params = (
                    task_id, filename,
                    result.filter_reasons[0].code,
                    result.filter_reasons[0].message,
                    json.dumps([r.__dict__ for r in result.filter_reasons])
                )
                DBConnection.execute(sql, params)
            
            # 更新图片状态
            sql = """
                UPDATE image_list 
                SET status = %s
                WHERE task_id = %s AND filename = %s
            """
            status = 'success' if not result.filter_reasons else 'failed'
            DBConnection.execute(sql, (status, task_id, filename))

            # 保存图片到目标文件夹
            image_path = os.path.join(task['input_dir'], filename)
            output_dir = os.path.join(task['output_dir'], "passed" if not result.filter_reasons else "filtered")
            if not os.path.exists(output_dir):
                os.makedirs(output_dir)
            shutil.copy2(image_path, output_dir) 
            
            # 更新任务统计信息
            sql = """
                UPDATE tasks 
                SET processed_files = processed_files + 1
                WHERE task_id = %s
            """
            DBConnection.execute(sql, (task_id,))
            
            # 避免频繁更新造成页面卡顿，达到100条更新一次   
            if self.processed_files % 100 == 0:
                # 发送更新信号
                self.update_task_stats.emit(task_id)
            
            return result
            
        except Exception as e:
            self.logger.error(f"处理图片失败: {e}", exc_info=True)
            return None

    def pause_task(self, task_id: str):
        """暂停任务"""
        sql = "UPDATE tasks SET status = 'paused' WHERE task_id = %s"
        DBConnection.execute(sql, (task_id,))

    def delete_task(self, task_id: str):
        """删除任务"""
        try:
            # 从数据库删除(会级联删除相关数据)
            sql = "DELETE FROM tasks WHERE task_id = %s"
            DBConnection.execute(sql, (task_id,))
            
            self.logger.info(f"删除任务成功: {task_id}")
                
        except Exception as e:
            self.logger.error(f"删除任务失败: {e}", exc_info=True)
            raise

    def get_task_stats(self, task_id: str) -> dict:
        """获取任务统计信息"""
        try:
            stats = {
                'total': 0,
                'processed': 0,
                'success': 0,
                'failed': 0
            }
            
            # 获取总数和处理数
            sql = """
                SELECT 
                    COUNT(*) as total,
                    SUM(CASE WHEN status IN ('success', 'failed') THEN 1 ELSE 0 END) as processed,
                    SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) as success,
                    SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed
                FROM image_list
                WHERE task_id = %s
            """
            result = DBConnection.execute(sql, (task_id,), fetch=True)[0]
            
            stats.update(result)
            return stats
            
        except Exception as e:
            self.logger.error(f"获取任务统计信息失败: {e}", exc_info=True)
            raise

    def get_filtered_results(self, task_id: str, page: int, page_size: int, filter_reason: str = None) -> tuple:
        """获取过滤结果"""
        try:
            offset = (page - 1) * page_size
            
            # 构建基础查询
            base_sql = """
                SELECT f.*, i.file_size 
                FROM failed_results f
                JOIN image_list i ON f.task_id = i.task_id AND f.filename = i.filename
                WHERE f.task_id = %s
            """
            count_sql = """
                SELECT COUNT(*) as count 
                FROM failed_results 
                WHERE task_id = %s
            """
            params = [task_id]
            
            # 添加过滤条件
            if filter_reason:
                base_sql += " AND f.filter_code = %s"
                count_sql += " AND filter_code = %s"
                params.append(filter_reason)
            
            # 添加分页
            base_sql += " LIMIT %s OFFSET %s"
            params.extend([page_size, offset])
            
            # 获取总数
            total = DBConnection.execute(count_sql, params[:-2], fetch=True)[0]['count']
            
            # 获取数据
            results = DBConnection.execute(base_sql, params, fetch=True)
            
            return results, total
            
        except Exception as e:
            self.logger.error(f"获取过滤结果失败: {e}", exc_info=True)
            raise

    def get_success_results(self, task_id: str, page: int, page_size: int) -> tuple:
        """获取成功结果"""
        try:
            offset = (page - 1) * page_size
            
            # 构建查询
            sql = """
                SELECT s.*, i.file_size 
                FROM success_results s
                JOIN image_list i ON s.task_id = i.task_id AND s.filename = i.filename
                WHERE s.task_id = %s
                LIMIT %s OFFSET %s
            """
            
            # 获取总数
            count_sql = """
                SELECT COUNT(*) as count 
                FROM success_results 
                WHERE task_id = %s
            """
            
            total = DBConnection.execute(count_sql, (task_id,), fetch=True)[0]['count']
            results = DBConnection.execute(sql, (task_id, page_size, offset), fetch=True)
            
            return results, total
            
        except Exception as e:
            self.logger.error(f"获取成功结果失败: {e}", exc_info=True)
            raise

    def get_all_filter_reasons(self, task_id: str) -> list:
        """获取所有过滤原因"""
        if task_id not in self.tasks:
            return []
            
        task = self.tasks[task_id]
        reasons = set()
        
        for result in task['failed_results'].values():
            for reason in result.get('filter_reasons', []):
                if 'message' in reason:
                    reasons.add(reason['message'])
        
        return sorted(list(reasons))

    def create_task(self, input_dir: str, output_dir: str, config: 'ProcessConfig') -> str:
        """创建新任务"""
        try:
            self.logger.info(f"开始创建任务: input_dir={input_dir}, output_dir={output_dir}")
            task_id = str(uuid.uuid4())
            
            # 创建 ProcessTask 对象
            task = ProcessTask(task_id, input_dir, output_dir, config)
            task['status'] = 'processing'  # 直接设为处理中
            task['start_time'] = datetime.now()
            task['total_files'] = self.get_total_images_files_of_input_dir(input_dir)
            
            # 创建任务数据目录和文件
            task_data_dir = self.data_dir / task_id
            task_data_dir.mkdir(exist_ok=True)
            
            self.logger.info(f"任务创建成功: task_id={task_id}")
            return task_id
            
        except Exception as e:
            self.logger.error(f"创建任务失败: {e}", exc_info=True)
            return None

    def get_task(self, task_id: str) -> Optional[ProcessTask]:
        """获取任务信息"""

        sql = "SELECT * FROM tasks WHERE task_id = %s"  
        task = DBConnection.execute(sql, (task_id,), fetch=True)[0]
        return task
    
    def get_all_tasks(self) -> List[ProcessTask]:
        """获取所有任务"""
        sql = "SELECT * FROM tasks"
        tasks = DBConnection.execute(sql, fetch=True)
        return tasks
    

    def analyze_image(self, image_path: str) -> ImageAnalysis:
        """分析图片"""
        try:
            # 读取图片
            img = ImageProcessor.imread_with_chinese_path(image_path)
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            
            # 计算清晰度分数
            laplacian_score = cv2.Laplacian(gray, cv2.CV_64F).var() / 100  # 归一化到0-1
            
            # 计算对比度分数
            contrast_score = gray.std() / 128  # 归一化到0-1
            
            # 计算亮度分数
            brightness = cv2.mean(gray)[0]
            brightness_score = 1 - abs(brightness - 128) / 128  # 归一化到0-1
            
            # 检测人脸并计算占比
            face_locations = face_recognition.face_locations(img)
            if face_locations:
                face = max(face_locations, key=lambda x: (x[2]-x[0])*(x[3]-x[1]))
                face_area = (face[2]-face[0]) * (face[3]-face[1])
                total_area = img.shape[0] * img.shape[1]
                face_ratio = face_area / total_area
                face_ratio_score = 1 - abs(0.3 - face_ratio) / 0.3  # 归一化到0-1
                
                # 判断图片类型
                height_ratio = img.shape[0] / img.shape[1]
                if height_ratio > 1.8:  # 高度是宽度的1.8倍以上
                    prompt = 'full_body'
                elif height_ratio > 1.3:  # 高度是宽度的1.3-1.8倍
                    prompt = 'cropped_arms'
                else:
                    prompt = 'upper_body'
            else:
                face_ratio_score = 0
                prompt = 'upper_body'
            
            return ImageAnalysis(
                laplacian_score=laplacian_score,
                contrast_score=contrast_score,
                face_ratio_score=face_ratio_score,
                brightness_score=brightness_score,
                prompt=prompt
            )
            
        except Exception as e:
            self.logger.error(f"分析图片失败: {e}")
            return ImageAnalysis()

    def create_thumbnail(self, image_path: str, thumb_path: str, size: int = 120):
        """创建缩略图"""
        try:
            img = Image.open(image_path)
            img.thumbnail((size, size))
            img.save(thumb_path, "JPEG")
        except Exception as e:
            self.logger.error(f"创建缩略图失败: {e}")

    def load_task(self, task_data: dict):
        """加载任务"""
        task = ProcessTask(
            task_data['task_id'],
            task_data['input_dir'],
            task_data['output_dir'],
            self.config
        )
        task['status'] = task_data['status']
        task['progress'] = task_data['progress']
        task['processed_files'] = task_data['processed_files']
        task['total_files'] = task_data['total_files']
        
        if task_data.get('start_time'):
            task['start_time'] = datetime.fromisoformat(task_data['start_time'])
        if task_data.get('end_time'):
            task['end_time'] = datetime.fromisoformat(task_data['end_time'])
        
        for filename, result_data in task_data['results'].items():
            result = ProcessResult(filename)
            result.status = result_data['status']
            result.filter_reasons = [
                FilterReason(r['code'], r['message'])
                for r in result_data['filter_reasons']
            ]
            result.analysis = result_data['analysis']
            result.processed_path = result_data.get('processed_path')
            if result_data.get('timestamp'):
                result['timestamp'] = datetime.fromisoformat(result_data['timestamp'])
            task['results'][filename] = result
        
        self.tasks[task_data['task_id']] = task
        
        return task 

    def remove_task(self, task_id: str):
        """删除任务"""
        try:
            # 从数据库查询任务
            sql = "SELECT * FROM tasks WHERE task_id = %s"
            task = DBConnection.execute(sql, (task_id,), fetch=True)
            
            if task:
                task = task[0]  # 获取第一条记录
                
                # 删除任务数据(会级联删除相关数据)
                sql = "DELETE FROM tasks WHERE task_id = %s"
                DBConnection.execute(sql, (task_id,))
                
                self.logger.info(f"删除任务成功: {task_id}")
                
        except Exception as e:
            self.logger.error(f"删除任务失败: {task_id}, 错误: {e}", exc_info=True)
            raise

    def auto_save_if_needed(self):
        """检查是否需要自动保存"""
        try:
            current_time = time.time()
            
            # 如果距离上次保存超过30秒，且有任务在处理中，则保存
            if (current_time - self.last_save_time) > 30:
                need_save = False
                
                # 检查是否有处理中的任务
                for task in self.tasks.values():
                    if task['status'] == 'processing':
                        need_save = True
                        break
                
                if need_save:
                    self.save_tasks()
                    self.last_save_time = current_time
                    self.logger.debug("执行自动保存")
                    
        except Exception as e:
            self.logger.error(f"自动保存检查失败: {e}")

    def save_task_state(self, task_id: str):
        """保存任务状态"""
        try:
            task = self.tasks.get(task_id)
            if task:
                state = {
                    'task_id': task_id,
                    'input_dir': task['input_dir'],
                    'output_dir': task['output_dir'],
                    'status': task['status'],
                    'progress': task['progress'],
                    'processed_files': task['processed_files'],
                    'total_files': task['total_files'],
                    'image_files': task['image_files'],  # 保存图片列表
                    'current_index': task['current_index'],  # 保存当前处理位置
                    'results': {
                        filename: {
                            'status': result['status'],
                            'processed_path': result.processed_path,
                            'filter_reasons': [
                                {'code': r.code, 'message': r.message}
                                for r in result.filter_reasons
                            ],
                            'analysis': result.analysis  # 保存分析结果
                        }
                        for filename, result in task['results'].items()
                    }
                }
                
                state_file = Path.home() / '.lora_filter' / f'task_{task_id}.json'
                state_file.parent.mkdir(parents=True, exist_ok=True)
                
                with open(state_file, 'w', encoding='utf-8') as f:
                    json.dump(state, f, ensure_ascii=False, indent=2)
                
                self.logger.info(f"保存任务状态: {task_id}")
        except Exception as e:
            self.logger.error(f"保存任务状态失败: {e}", exc_info=True)

    def load_task_states(self):
        """加载所有任务状态"""
        try:
            for state_file in (Path.home() / '.lora_filter').glob('task_*.json'):
                try:
                    with open(state_file, 'r', encoding='utf-8') as f:
                        state = json.load(f)
                        
                    # 检查目录是否存在
                    if not os.path.exists(state['input_dir']) or not os.path.exists(state['output_dir']):
                        continue
                        
                    # 恢复任务
                    task = self.create_task(state['input_dir'], state['output_dir'])
                    task['status'] = state['status']
                    task['progress'] = state['progress']
                    task['processed_files'] = state['processed_files']
                    task['total_files'] = state['total_files']
                    task['image_files'] = state['image_files']
                    task['current_index'] = state['current_index']
                    
                    # 恢复处理结果
                    for filename, result_data in state['results'].items():
                        result = ProcessResult(filename)
                        result.status = result_data['status']
                        result.processed_path = result_data['processed_path']
                        result['filter_reasons'] = [
                            FilterReason(r['code'], r['message'])
                            for r in result_data['filter_reasons']
                        ]
                        result['analysis'] = result_data.get('analysis')
                        task['results'][filename] = result
                    
                    # 如果任务未完成，添加到处理队列
                    if task['status'] in ['pending', 'processing', 'paused']:
                        self.task_queue.put(task['task_id'])
                    
                except Exception as e:
                    self.logger.error(f"加载任务状态失败: {state_file}, 错误: {e}", exc_info=True)
                    
        except Exception as e:
            self.logger.error(f"加载任务状态失败: {e}", exc_info=True) 

    def calculate_file_hash(self, filepath: str) -> str:
        """计算文件hash值"""
        try:
            with open(filepath, 'rb') as f:
                return hashlib.md5(f.read()).hexdigest()
        except Exception as e:
            self.logger.error(f"计算文件hash失败: {e}")
            return ""

    def scan_directory(self, input_dir: str) -> Dict[str, ImageInfo]:
        """扫描目录获取图片列表"""
        image_list = {}
        try:
            for root, _, files in os.walk(input_dir):
                for file in files:
                    if file.lower().endswith(('.jpg', '.jpeg', '.png')):
                        filepath = os.path.join(root, file)
                        size = os.path.getsize(filepath)
                        file_hash = self.calculate_file_hash(filepath)
                        image_list[file] = ImageInfo(
                            size=size,
                            hash=file_hash
                        )
        except Exception as e:
            self.logger.error(f"扫描目录失败: {e}")
        return image_list
    
    def get_total_images_files_of_input_dir(self, input_dir: str) -> int:
        """获取input_dir的图片文件数量"""
        # 列出目录中的所有内容
        all_items = os.listdir(input_dir)
        # 过滤出特定扩展名的文件
        file_count = sum(
            item.lower().endswith(".jpg") or item.lower().endswith(".jpeg") or item.lower().endswith(".png") and os.path.isfile(os.path.join(input_dir, item))
            for item in all_items
        )
        return file_count

    def create_task_base(self, input_dir: str, output_dir: str) -> dict:
        """创建基础任务信息"""
        try:
            task_id = str(uuid.uuid4())
            task = {
                'task_id': task_id,
                'input_dir': input_dir,
                'output_dir': output_dir,
                'status': 'processing',
                'total_files': self.get_total_images_files_of_input_dir(input_dir),
                'processed_files': 0,
                'total_files_size': 0,
                'start_time': datetime.now().isoformat(),
                'end_time': None
            }
            
            # 保存到数据库
            sql = """
                INSERT INTO tasks (
                    task_id, input_dir, output_dir, status,
                    total_files, processed_files, total_files_size,
                    start_time
                ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
            """
            params = (
                task_id, input_dir, output_dir, 'processing',
                task['total_files'], 0, 0, task['start_time']
            )
            
            DBConnection.execute(sql, params)
            return task
            
        except Exception as e:
            self.logger.error(f"创建任务失败: {e}", exc_info=True)
            raise

    def save_image_list(self, task_id: str):
        """持续扫描目录并添加到处理队列"""
        try:
            # 获取任务信息
            sql = "SELECT * FROM tasks WHERE task_id = %s"
            task = DBConnection.execute(sql, (task_id,), fetch=True)[0]
            
            # 先获取所有图片文件路径
            image_files = []
            for root, _, files in os.walk(task['input_dir']):
                for file in files:
                    if file.lower().endswith(('.jpg', '.jpeg', '.png')):
                        image_files.append((root, file))
            
            total_files = len(image_files)
            self.logger.info(f"找到 {total_files} 个图片文件")
            
            # 更新任务总文件数
            sql = """
                UPDATE tasks 
                SET total_files = %s 
                WHERE task_id = %s
            """
            DBConnection.execute(sql, (total_files, task_id))
            
            # 分批处理文件
            batch_size = 200
            processed = 0
            image_list_data = []
            
            for root, file in image_files:
                try:
                    filepath = os.path.join(root, file)
                    
                    # 检查文件是否存在
                    if not os.path.exists(filepath):
                        self.logger.warning(f"文件不存在: {filepath}")
                        continue
                    
                    # 获取文件信息
                    size = os.path.getsize(filepath)
                    file_hash = self.calculate_file_hash(filepath)

                    # 生成缩略图
                    thumb_path = os.path.join(task['output_dir'], 'thumb', file)
                    # 保证缩略图路径存在
                    os.makedirs(os.path.dirname(thumb_path), exist_ok=True)
                    self.create_thumbnail(filepath, thumb_path)
                    
                    # 准备数据
                    image_list_data.append({
                        'task_id': task_id,
                        'filename': file,
                        'file_size': size,
                        'file_hash': file_hash,
                        'thumb_path': "thumb/" + file,
                        'flip_image_path': None,
                        'crop_image_path': None,
                        'status': 'wait',
                        'created_at': datetime.now().isoformat()
                    })
                    
                    processed += 1
                    
                    # 批量插入数据库
                    if len(image_list_data) >= batch_size:
                        self._batch_insert_images(image_list_data)
                        image_list_data = []
                        
                        self.logger.info(f"已扫描并添加 {processed}/{total_files} 个文件保存到数据库")
                except Exception as e:
                    self.logger.error(f"处理文件失败: {file}, 错误: {e}", exc_info=True)
                    continue
            
            # 处理剩余的数据
            if image_list_data:
                self._batch_insert_images(image_list_data)
                
            self.logger.info(f"目录扫描完成: task_id={task_id}, total_files={total_files}, processed={processed}")
            
        except Exception as e:
            self.logger.error(f"扫描目录失败: task_id={task_id}, error={e}", exc_info=True)

    def _batch_insert_images(self, image_data: list):
        """批量插入图片数据"""
        try:
            conn = DBConnection.get_connection()
            cursor = conn.cursor()
            
            try:
                sql = """
                    INSERT INTO image_list (
                        task_id, filename, file_size, file_hash, status, created_at
                    ) VALUES (
                        %(task_id)s, %(filename)s, %(file_size)s, %(file_hash)s, %(status)s, %(created_at)s
                    ) ON DUPLICATE KEY UPDATE
                        file_size = VALUES(file_size),
                        file_hash = VALUES(file_hash),
                        status = VALUES(status),
                        created_at = VALUES(created_at)
                """
                cursor.executemany(sql, image_data)
                conn.commit()
                
            except Exception as e:
                conn.rollback()
                raise e
                
            finally:
                cursor.close()
                conn.close()
                
        except Exception as e:
            self.logger.error(f"批量插入图片数据失败: {e}", exc_info=True)
            raise

    def update_task_status(self, task_id: str, status: str):
        """更新任务状态"""
        try:
            if task_id in self.tasks:
                self.tasks[task_id]['status'] = status
                if status == 'completed':
                    self.tasks[task_id]['end_time'] = datetime.now().isoformat()
                
                # 更新数据库
                sql = """
                    UPDATE tasks 
                    SET status = %s, end_time = %s 
                    WHERE task_id = %s
                """
                params = (
                    status,
                    self.tasks[task_id].get('end_time'),
                    task_id
                )
                DBConnection.execute(sql, params)
                
        except Exception as e:
            self.logger.error(f"更新任务状态失败: {e}", exc_info=True)
            raise

    def get_task_info(self, task_id: str) -> dict:
        """获取任务信息"""
        try:
            sql = "SELECT * FROM tasks WHERE task_id = %s"
            task = DBConnection.execute(sql, (task_id,), fetch=True)
            return task[0] if task else None
            
        except Exception as e:
            self.logger.error(f"获取任务信息失败: {e}", exc_info=True)
            raise

    def load_all_tasks(self):
        """从数据库加载所有任务数据"""
        try:
            self.logger.info("开始加载任务数据")
            
            # 加载任务基本信息
            sql = "SELECT * FROM tasks"
            tasks = DBConnection.execute(sql, fetch=True)
            
            for task in tasks:
                self.tasks[task['task_id']] = task
                
                # 加载图片列表
                sql = "SELECT * FROM image_list WHERE task_id = %s"
                images = DBConnection.execute(sql, (task['task_id'],), fetch=True)
                
                self.image_lists[task['task_id']] = {
                    img['filename']: ImageInfo(
                        size=img['file_size'],
                        status=img['status'],
                        hash=img['file_hash'],
                        timestamp=img['created_at']
                    ) for img in images
                }
                
                # 加载成功结果
                sql = "SELECT * FROM success_results WHERE task_id = %s"
                successes = DBConnection.execute(sql, (task['task_id'],), fetch=True)
                
                self.success_results[task['task_id']] = {}
                for s in successes:
                    result = ProcessResult()
                    result.processed_path = s['processed_path']
                    result.face_count = s['face_count']
                    result.face_ratio = s['face_ratio']
                    result.analysis = ImageAnalysis(
                        brightness_score=s['brightness_score'],
                        contrast_score=s['contrast_score'],
                        blur_score=s['blur_score']
                    )
                    self.success_results[task['task_id']][s['filename']] = result
                
                # 加载失败结果
                sql = "SELECT * FROM failed_results WHERE task_id = %s"
                failures = DBConnection.execute(sql, (task['task_id'],), fetch=True)
                
                self.failed_results[task['task_id']] = {}
                for f in failures:
                    result = ProcessResult()
                    filter_details = json.loads(f['filter_details'])
                    result.filter_reasons = [
                        FilterReason(**reason) for reason in filter_details
                    ]
                    self.failed_results[task['task_id']][f['filename']] = result
            
            self.logger.info(f"加载了 {len(self.tasks)} 个任务")
            
        except Exception as e:
            self.logger.error(f"加载任务数据失败: {e}", exc_info=True)
            raise

    def _auto_save_loop(self):
        """自动保存循环"""
        while self.running:
            try:
                if time.time() - self.last_save_time >= self.save_interval:
                    # 保存任务基础信息
                    with open(self.tasks_file, 'w', encoding='utf-8') as f:
                        json.dump(self.tasks, f, ensure_ascii=False, indent=2)
                    
                    # 保存每个任务的详细数据
                    for task_id in list(self.tasks.keys()):  # 创建副本进行迭代
                        task_dir = self.data_dir / task_id
                        task_dir.mkdir(exist_ok=True)
                        
                        # 保存图片列表
                        if task_id in self.image_lists:
                            # 创建字典副本进行迭代
                            items = list(self.image_lists[task_id].items())
                            with open(task_dir / 'image_list.jsonl', 'a', encoding='utf-8') as f:
                                for filename, info in items:
                                    if not hasattr(info, '_saved'):  # 只保存新数据
                                        f.write(json.dumps({
                                            'filename': filename,
                                            **info.to_dict()
                                        }, ensure_ascii=False) + '\n')
                                        info._saved = True
                        
                        # 保存处理结果时也使用字典副本
                        if task_id in self.success_results:
                            items = list(self.success_results[task_id].items())
                            with open(task_dir / 'success_results.jsonl', 'a', encoding='utf-8') as f:
                                for filename, result in items:
                                    if not hasattr(result, '_saved'):
                                        f.write(json.dumps({
                                            'filename': filename,
                                            **result.to_dict()
                                        }, ensure_ascii=False) + '\n')
                                        result._saved = True
                        
                        if task_id in self.failed_results:
                            items = list(self.failed_results[task_id].items())
                            with open(task_dir / 'failed_results.jsonl', 'a', encoding='utf-8') as f:
                                for filename, result in items:
                                    if not hasattr(result, '_saved'):
                                        f.write(json.dumps({
                                            'filename': filename,
                                            **result.to_dict()
                                        }, ensure_ascii=False) + '\n')
                                        result._saved = True
                    
                    self.last_save_time = time.time()
                    
                time.sleep(0.1)  # 避免过度占用CPU
                
            except Exception as e:
                self.logger.error(f"自动保存失败: {e}", exc_info=True)
                time.sleep(1)  # 出错时等待较长时间 

    def save_tasks(self):
        """保存任务数据到数据库"""
        try:
            sql = "SELECT * FROM tasks"
            tasks = DBConnection.execute(sql, fetch=True)
            for task in tasks:
                # 更新任务基本信息
                sql = """
                    INSERT INTO tasks (
                        task_id, status, input_dir, output_dir, 
                        total_files, processed_files, total_files_size,
                        start_time, end_time
                    ) VALUES (
                        %s, %s, %s, %s, %s, %s, %s, %s, %s
                    ) ON DUPLICATE KEY UPDATE
                        status = VALUES(status),
                        total_files = VALUES(total_files),
                        processed_files = VALUES(processed_files),
                        total_files_size = VALUES(total_files_size),
                        start_time = VALUES(start_time),
                        end_time = VALUES(end_time)
                """
                params = (
                    task['task_id'],
                    task['status'],
                    task['input_dir'],
                    task['output_dir'],
                    task['total_files'],
                    task['processed_files'],
                    task['total_files_size'],
                    task['start_time'],
                    task['end_time']
                )
                DBConnection.execute(sql, params)
                
                # 保存图片列表
                sql = "SELECT * FROM image_list WHERE task_id = %s" 
                image_list = DBConnection.execute(sql, (task['task_id'],), fetch=True)
                if image_list:
                    for img in image_list:
                        sql = """
                            INSERT INTO image_list (
                                task_id, filename, file_size, file_hash, status
                            ) VALUES (
                                %s, %s, %s, %s, %s
                            ) ON DUPLICATE KEY UPDATE
                                status = VALUES(status)
                        """
                        params = (
                            task['task_id'] ,
                            img['filename'],
                            img['file_size'],
                            img['file_hash'],
                            img['status']
                        )
                        DBConnection.execute(sql, params)
                
                # 保存成功结果
                if task['task_id'] in self.success_results:
                    for filename, result in self.success_results[task['task_id']].items():
                        sql = """
                            INSERT INTO success_results (
                                task_id, filename, processed_path, quality_score,
                                face_count, face_ratio, brightness_score,
                                contrast_score, blur_score
                            ) VALUES (
                                %s, %s, %s, %s, %s, %s, %s, %s, %s
                            ) ON DUPLICATE KEY UPDATE
                                processed_path = VALUES(processed_path),
                                quality_score = VALUES(quality_score)
                        """
                        params = (
                            task['task_id'],
                            filename,
                            result.processed_path,
                            result.analysis.quality_score,
                            result.face_count,
                            result.face_ratio,
                            result.analysis.brightness_score,
                            result.analysis.contrast_score,
                            result.analysis.blur_score
                        )
                        DBConnection.execute(sql, params)
                
                # 保存失败结果
                if task['task_id'] in self.failed_results:
                    for filename, result in self.failed_results[task['task_id']].items():
                        sql = """
                            INSERT INTO failed_results (
                                task_id, filename, filter_code,
                                filter_message, filter_details
                            ) VALUES (
                                %s, %s, %s, %s, %s
                            ) ON DUPLICATE KEY UPDATE
                                filter_code = VALUES(filter_code),
                                filter_message = VALUES(filter_message),
                                filter_details = VALUES(filter_details)
                        """
                        params = (
                            task['task_id'],
                            filename,
                            result.filter_reasons[0].code if result.filter_reasons else '',
                            result.filter_reasons[0].message if result.filter_reasons else '',
                            json.dumps([r.__dict__ for r in result.filter_reasons])
                        )
                        DBConnection.execute(sql, params)
                    
            self.logger.info("保存任务数据成功")
            
        except Exception as e:
            self.logger.error(f"保存任务数据失败: {e}", exc_info=True)
            raise 